
    window.onload = async () => {
    const env = {
        serviceUrl: "https://vjmap.com/server/api/v1",
        accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6MiwiVXNlcm5hbWUiOiJhZG1pbjEiLCJOaWNrTmFtZSI6ImFkbWluMSIsIkF1dGhvcml0eUlkIjoiYWRtaW4iLCJCdWZmZXJUaW1lIjo4NjQwMCwiZXhwIjo0ODEzMjY3NjM3LCJpc3MiOiJ2am1hcCIsIm5iZiI6MTY1OTY2NjYzN30.cDXCH2ElTzU2sQU36SNHWoTYTAc4wEkVIXmBAIzWh6M",
        exampleMapId: "sys_zp",
        assetsPath: "../../assets/",
        ...__env__ // 如果您已私有化部署，需要连接已部署的服务器地址和token，请打开js/env.js,修改里面的参数
    };
    try {
        // 在线效果查看地址: https://vjmap.com/map3d/demo/#/demo/map/threejs/187threeWebglgpgpuprotoplanet
        // --gpgpu_protoplanet--
        // 下面代码参考threejs官方示例改写 https://threejs.org/examples/#webgl_gpgpu_protoplanet
        let svc = new vjmap3d.Service(env.serviceUrl, env.accessToken);
        let app = new vjmap3d.App(svc, {
            container: "map", // 容器id
            scene: {
                defaultLights: false
            },
            stat: { show: true, left: "0" },
            camera: {
                fov: 75,
                near: 5,
                far: 15000,
                position: [0, 120, 400 ]
            },
            control: {
                minDistance: 100,
                maxDistance: 1000
            }
        })
        let scene = app.scene, camera = app.camera, renderer = app.renderer;
        
        
        // Texture width for simulation (each texel is a debris particle)
        const WIDTH = 64;
        
        let geometry;
        
        const PARTICLES = WIDTH * WIDTH;
        
        let gpuCompute;
        let velocityVariable;
        let positionVariable;
        let velocityUniforms;
        let particleUniforms;
        let effectController;
        
        init();
        
        function init() {
        
        
            effectController = {
                // Can be changed dynamically
                gravityConstant: 100.0,
                density: 0.45,
        
                // Must restart simulation
                radius: 300,
                height: 8,
                exponent: 0.4,
                maxMass: 15.0,
                velocity: 70,
                velocityExponent: 0.2,
                randVelocity: 0.001
            };
        
            initComputeRenderer();
        
        
            initGUI();
        
            initProtoplanets();
        
            dynamicValuesChanger();
        
            app.signal.onAppUpdate.add(render)
        }
        
        function initComputeRenderer() {
        
            gpuCompute = new GPUComputationRenderer( WIDTH, WIDTH, renderer );
        
            const dtPosition = gpuCompute.createTexture();
            const dtVelocity = gpuCompute.createTexture();
        
            fillTextures( dtPosition, dtVelocity );
        
            velocityVariable = gpuCompute.addVariable( 'textureVelocity', /* glsl */`
            // For PI declaration:
        			#include <common>
        
                #define delta ( 1.0 / 60.0 )
        
                uniform float gravityConstant;
                uniform float density;
        
                const float width = resolution.x;
                const float height = resolution.y;
        
                float radiusFromMass( float mass ) {
                    // Calculate radius of a sphere from mass and density
                    return pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
                }
        
                void main()	{
        
                    vec2 uv = gl_FragCoord.xy / resolution.xy;
                    float idParticle = uv.y * resolution.x + uv.x;
        
                    vec4 tmpPos = texture2D( texturePosition, uv );
                    vec3 pos = tmpPos.xyz;
        
                    vec4 tmpVel = texture2D( textureVelocity, uv );
                    vec3 vel = tmpVel.xyz;
                    float mass = tmpVel.w;
        
                    if ( mass > 0.0 ) {
        
                        float radius = radiusFromMass( mass );
        
                        vec3 acceleration = vec3( 0.0 );
        
                        // Gravity interaction
                        for ( float y = 0.0; y < height; y++ ) {
        
                            for ( float x = 0.0; x < width; x++ ) {
        
                                vec2 secondParticleCoords = vec2( x + 0.5, y + 0.5 ) / resolution.xy;
                                vec3 pos2 = texture2D( texturePosition, secondParticleCoords ).xyz;
                                vec4 velTemp2 = texture2D( textureVelocity, secondParticleCoords );
                                vec3 vel2 = velTemp2.xyz;
                                float mass2 = velTemp2.w;
        
                                float idParticle2 = secondParticleCoords.y * resolution.x + secondParticleCoords.x;
        
                                if ( idParticle == idParticle2 ) {
                                    continue;
                                }
        
                                if ( mass2 == 0.0 ) {
                                    continue;
                                }
        
                                vec3 dPos = pos2 - pos;
                                float distance = length( dPos );
                                float radius2 = radiusFromMass( mass2 );
        
                                if ( distance == 0.0 ) {
                                    continue;
                                }
        
                                // Checks collision
        
                                if ( distance < radius + radius2 ) {
        
                                    if ( idParticle < idParticle2 ) {
        
                                        // This particle is aggregated by the other
                                        vel = ( vel * mass + vel2 * mass2 ) / ( mass + mass2 );
                                        mass += mass2;
                                        radius = radiusFromMass( mass );
        
                                    }
                                    else {
        
                                        // This particle dies
                                        mass = 0.0;
                                        radius = 0.0;
                                        vel = vec3( 0.0 );
                                        break;
        
                                    }
        
                                }
        
                                float distanceSq = distance * distance;
        
                                float gravityField = gravityConstant * mass2 / distanceSq;
        
                                gravityField = min( gravityField, 1000.0 );
        
                                acceleration += gravityField * normalize( dPos );
        
                            }
        
                            if ( mass == 0.0 ) {
                                break;
                            }
                        }
        
                        // Dynamics
                        vel += delta * acceleration;
        
                    }
        
                    gl_FragColor = vec4( vel, mass );
        
                }
        `, dtVelocity );
            positionVariable = gpuCompute.addVariable( 'texturePosition', /* glsl */`
                 #define delta ( 1.0 / 60.0 )
        
                void main() {
        
                    vec2 uv = gl_FragCoord.xy / resolution.xy;
        
                    vec4 tmpPos = texture2D( texturePosition, uv );
                    vec3 pos = tmpPos.xyz;
        
                    vec4 tmpVel = texture2D( textureVelocity, uv );
                    vec3 vel = tmpVel.xyz;
                    float mass = tmpVel.w;
        
                    if ( mass == 0.0 ) {
                        vel = vec3( 0.0 );
                    }
        
                    // Dynamics
                    pos += vel * delta;
        
                    gl_FragColor = vec4( pos, 1.0 );
        
                }
            `, dtPosition );
        
            gpuCompute.setVariableDependencies( velocityVariable, [ positionVariable, velocityVariable ] );
            gpuCompute.setVariableDependencies( positionVariable, [ positionVariable, velocityVariable ] );
        
            velocityUniforms = velocityVariable.material.uniforms;
        
            velocityUniforms[ 'gravityConstant' ] = { value: 0.0 };
            velocityUniforms[ 'density' ] = { value: 0.0 };
        
            const error = gpuCompute.init();
        
            if ( error !== null ) {
        
                console.error( error );
        
            }
        
        }
        
        function restartSimulation() {
        
            const dtPosition = gpuCompute.createTexture();
            const dtVelocity = gpuCompute.createTexture();
        
            fillTextures( dtPosition, dtVelocity );
        
            gpuCompute.renderTexture( dtPosition, positionVariable.renderTargets[ 0 ] );
            gpuCompute.renderTexture( dtPosition, positionVariable.renderTargets[ 1 ] );
            gpuCompute.renderTexture( dtVelocity, velocityVariable.renderTargets[ 0 ] );
            gpuCompute.renderTexture( dtVelocity, velocityVariable.renderTargets[ 1 ] );
        
        }
        
        function initProtoplanets() {
        
            geometry = new THREE.BufferGeometry();
        
            const positions = new Float32Array( PARTICLES * 3 );
            let p = 0;
        
            for ( let i = 0; i < PARTICLES; i ++ ) {
        
                positions[ p ++ ] = ( Math.random() * 2 - 1 ) * effectController.radius;
                positions[ p ++ ] = 0; //( Math.random() * 2 - 1 ) * effectController.radius;
                positions[ p ++ ] = ( Math.random() * 2 - 1 ) * effectController.radius;
        
            }
        
            const uvs = new Float32Array( PARTICLES * 2 );
            p = 0;
        
            for ( let j = 0; j < WIDTH; j ++ ) {
        
                for ( let i = 0; i < WIDTH; i ++ ) {
        
                    uvs[ p ++ ] = i / ( WIDTH - 1 );
                    uvs[ p ++ ] = j / ( WIDTH - 1 );
        
                }
        
            }
        
            geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
            geometry.setAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
        
            particleUniforms = {
                'texturePosition': { value: null },
                'textureVelocity': { value: null },
                'cameraConstant': { value: getCameraConstant( camera ) },
                'density': { value: 0.0 }
            };
        
            // THREE.ShaderMaterial
            const material = new THREE.ShaderMaterial( {
                uniforms: particleUniforms,
                vertexShader: /* glsl */`
                // For PI declaration:
        			#include <common>
        
                    uniform sampler2D texturePosition;
                    uniform sampler2D textureVelocity;
        
                    uniform float cameraConstant;
                    uniform float density;
        
                    varying vec4 vColor;
        
                    float radiusFromMass( float mass ) {
                        // Calculate radius of a sphere from mass and density
                        return pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
                    }
        
        
                    void main() {
        
        
                        vec4 posTemp = texture2D( texturePosition, uv );
                        vec3 pos = posTemp.xyz;
        
                        vec4 velTemp = texture2D( textureVelocity, uv );
                        vec3 vel = velTemp.xyz;
                        float mass = velTemp.w;
        
                        vColor = vec4( 1.0, mass / 250.0, 0.0, 1.0 );
        
                        vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
        
                        // Calculate radius of a sphere from mass and density
                        //float radius = pow( ( 3.0 / ( 4.0 * PI ) ) * mass / density, 1.0 / 3.0 );
                        float radius = radiusFromMass( mass );
        
                        // Apparent size in pixels
                        if ( mass == 0.0 ) {
                            gl_PointSize = 0.0;
                        }
                        else {
                            gl_PointSize = radius * cameraConstant / ( - mvPosition.z );
                        }
        
                        gl_Position = projectionMatrix * mvPosition;
        
                    }
                `,
                fragmentShader: /* glsl */`
                varying vec4 vColor;
        
                void main() {
        
                    if ( vColor.y == 0.0 ) discard;
        
                    float f = length( gl_PointCoord - vec2( 0.5, 0.5 ) );
                    if ( f > 0.5 ) {
                        discard;
                    }
                    gl_FragColor = vColor;
        
                }
                `
            } );
        
            const particles = new THREE.Points( geometry, material );
            particles.matrixAutoUpdate = false;
            particles.updateMatrix();
        
            scene.add( particles );
        
        }
        
        function fillTextures( texturePosition, textureVelocity ) {
        
            const posArray = texturePosition.image.data;
            const velArray = textureVelocity.image.data;
        
            const radius = effectController.radius;
            const height = effectController.height;
            const exponent = effectController.exponent;
            const maxMass = effectController.maxMass * 1024 / PARTICLES;
            const maxVel = effectController.velocity;
            const velExponent = effectController.velocityExponent;
            const randVel = effectController.randVelocity;
        
            for ( let k = 0, kl = posArray.length; k < kl; k += 4 ) {
        
                // Position
                let x, z, rr;
        
                do {
        
                    x = ( Math.random() * 2 - 1 );
                    z = ( Math.random() * 2 - 1 );
                    rr = x * x + z * z;
        
                } while ( rr > 1 );
        
                rr = Math.sqrt( rr );
        
                const rExp = radius * Math.pow( rr, exponent );
        
                // Velocity
                const vel = maxVel * Math.pow( rr, velExponent );
        
                const vx = vel * z + ( Math.random() * 2 - 1 ) * randVel;
                const vy = ( Math.random() * 2 - 1 ) * randVel * 0.05;
                const vz = - vel * x + ( Math.random() * 2 - 1 ) * randVel;
        
                x *= rExp;
                z *= rExp;
                const y = ( Math.random() * 2 - 1 ) * height;
        
                const mass = Math.random() * maxMass + 1;
        
                // Fill in texture values
                posArray[ k + 0 ] = x;
                posArray[ k + 1 ] = y;
                posArray[ k + 2 ] = z;
                posArray[ k + 3 ] = 1;
        
                velArray[ k + 0 ] = vx;
                velArray[ k + 1 ] = vy;
                velArray[ k + 2 ] = vz;
                velArray[ k + 3 ] = mass;
        
            }
        
        }
        
        
        function dynamicValuesChanger() {
        
            velocityUniforms[ 'gravityConstant' ].value = effectController.gravityConstant;
            velocityUniforms[ 'density' ].value = effectController.density;
            particleUniforms[ 'density' ].value = effectController.density;
        
        }
        
        function initGUI() {
        
            const gui = new GUI( { width: 280 } );
        
            const folder1 = gui.addFolder( 'Dynamic parameters' );
        
            folder1.add( effectController, 'gravityConstant', 0.0, 1000.0, 0.05 ).onChange( dynamicValuesChanger );
            folder1.add( effectController, 'density', 0.0, 10.0, 0.001 ).onChange( dynamicValuesChanger );
        
            const folder2 = gui.addFolder( 'Static parameters' );
        
            folder2.add( effectController, 'radius', 10.0, 1000.0, 1.0 );
            folder2.add( effectController, 'height', 0.0, 50.0, 0.01 );
            folder2.add( effectController, 'exponent', 0.0, 2.0, 0.001 );
            folder2.add( effectController, 'maxMass', 1.0, 50.0, 0.1 );
            folder2.add( effectController, 'velocity', 0.0, 150.0, 0.1 );
            folder2.add( effectController, 'velocityExponent', 0.0, 1.0, 0.01 );
            folder2.add( effectController, 'randVelocity', 0.0, 50.0, 0.1 );
        
            const buttonRestart = {
                restartSimulation: function () {
        
                    restartSimulation();
        
                }
            };
        
            folder2.add( buttonRestart, 'restartSimulation' );
        
            folder1.open();
            folder2.open();
        
        }
        
        function getCameraConstant( camera ) {
        
            return app.containerSize.height / ( Math.tan( THREE.MathUtils.DEG2RAD * 0.5 * camera.fov ) / camera.zoom );
        
        }
        
        
        function render() {
        
            gpuCompute.compute();
        
            particleUniforms[ 'texturePosition' ].value = gpuCompute.getCurrentRenderTarget( positionVariable ).texture;
            particleUniforms[ 'textureVelocity' ].value = gpuCompute.getCurrentRenderTarget( velocityVariable ).texture;
        
           
        
        }
        
    }
    catch (e) {
        console.error(e);
    }
};